using System;
using System.Collections.Generic;
using System.Text;
using C5;

namespace CoreCalc.SheetDefinedFunctions.CompOnlyNeededCells
{
  /// <summary>
  /// This class is for creating dependency based on non-strict functions
  /// </summary>
  /// <example>If in A4 the following function exists IF(A1=12;A2;B1) then A2 
  /// and B1 should depend on A1 (unless A1 somehow depends on A2 or B1)</example>
  public class CreateDependency : CodeGenerate
  {
    private DependencyTree dependencies;

    public CreateDependency(DependencyTree dependencies)
    {
      this.dependencies = dependencies;
    }

    public void TraverseTree()
    {
      List<FullCellAddr> allNodes = dependencies.GetAllNodes();

      foreach (FullCellAddr node in allNodes)
      {
        Cell cell;
        if (node.TryGetCell(out cell))
        {
          Formula formula = cell as Formula;
          if (formula != null)
          {
            Expr currentExpr = formula.Expr;

            Dictionary<FullCellAddr, HashSet<FullCellAddr>> ifSentenceDependencies;
            ifSentenceDependencies = FindIfSentences.GetDependencies(node, currentExpr);

            foreach (System.Collections.Generic.KeyValuePair<FullCellAddr, HashSet<FullCellAddr>> pair in ifSentenceDependencies)
            {
              // sestoft: what??!!
              if (pair.Key.row == 41 && pair.Key.col == 1)
              {
              }
              //Check that this adress do not support any of the other (avoid loop)
              //TODO: HACK: inserted a check for that the if sentence does not depend on itself 
              //that is a check for that there exist a cell reference A for a if sentence 
              //IF(e1, e2, e3) where A is in both e1 and either e2 or e3
              if (dependencies.HasDecendend(pair.Key, pair.Value))
              {
                //This will make sure that the cell is always calculated (and no check beforehand)
                ConditionForCell.AddAlwaysTrue(pair.Key);
              }
              else if (pair.Key.Equals(pair.Value))
              {
                ConditionForCell.AddAlwaysTrue(pair.Key);
              }
              else
              {
                foreach (FullCellAddr addr in pair.Value)
                {
                  if (!addr.Equals(pair.Key))
                    dependencies.AddChild(addr, pair.Key);
                }
              }
            }

          }
        }
      }
    }

    private class FindIfSentences : IExpressionVisitor
    {
      /// <summary>
      /// Maps from cells that depend to the cells dependend on
      /// </summary>
      private readonly Dictionary<FullCellAddr, HashSet<FullCellAddr>> dependenciesFromIfSentences;
      private readonly FullCellAddr currentCell;

      private FindIfSentences(FullCellAddr currentCell)
      {
        dependenciesFromIfSentences = new Dictionary<FullCellAddr, HashSet<FullCellAddr>>();
        this.currentCell = currentCell;
      }

      /// <summary>
      /// Creates a dictionary mapping from a dependend cell A to the cells that a depend upon
      /// </summary>
      /// <param name="currentAdress"></param>
      /// <param name="expr"></param>
      /// <returns></returns>
      public static Dictionary<FullCellAddr, HashSet<FullCellAddr>> GetDependencies(
          FullCellAddr currentAdress, Expr expr)
      {
        FindIfSentences finder = new FindIfSentences(currentAdress);
        expr.VisitorCall(finder);
        return finder.dependenciesFromIfSentences;
      }

      private CustomizedAddressCollector.AddressFunction GetFunction<T>(T referenceList) where T : C5.ICollection<FullCellAddr>
      {
        return delegate(CellArea area) { referenceList.AddAll(area.ToArray(currentCell)); };
      }

      public void CallVisitor(CellArea cellArea) { }

      public void CallVisitor(CellRef cellRef) { }

      public void CallVisitor(Expr expr) { }

      public void CallVisitor(NumberConst numbConst) { }

      public void CallVisitor(TextConst textConst) { }

      public void CallVisitor(FunCall funCall)
      {
        if (funCall.function.name == IfExpression.instance.name && funCall.es.Length == 3)
        {

          HashSet<FullCellAddr> determineCells = new HashSet<FullCellAddr>();
          CustomizedAddressCollector.GetAdresses(GetFunction(determineCells), funCall.es[0]);

          HashSet<FullCellAddr> dependingCells = new HashSet<FullCellAddr>();
          CustomizedAddressCollector.AddressFunction func = GetFunction(dependingCells);
          CustomizedAddressCollector.GetAdresses(func, funCall.es[1]);
          CustomizedAddressCollector.GetAdresses(func, funCall.es[2]);

          foreach (FullCellAddr cell in dependingCells)
          {
            HashSet<FullCellAddr> dependingOn;
            if (this.dependenciesFromIfSentences.TryGetValue(cell, out dependingOn))
              dependingOn.AddAll(determineCells);
            else
              dependenciesFromIfSentences.Add(cell, determineCells);
          }
        }

        foreach (Expr e in funCall.es)
          e.VisitorCall(this);
      }
    }
  }
}
